home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / drdobbs / 1989 / 09 / green.lst < prev    next >
File List  |  1989-07-27  |  18KB  |  507 lines

  1. _80386 PROTECTED MODE AND MULTITASKING_
  2. by Tom Green
  3.  
  4.  
  5. [LISTIN╟ ONE]
  6.  
  7. /************************************************************************/
  8. /*      386.H - structures etc. for the 80386                           */
  9. /*      By Tom Green                                                    */
  10. /************************************************************************/
  11.  
  12. /* all of these structures are processor dependant, so  */
  13. /* you must set code generation for byte alignment */
  14.  
  15.  
  16. /* generic descriptor - data, code, system, TSS */
  17.  
  18. typedef struct descriptor{
  19.     unsigned int limit_lo;
  20.     unsigned int base_lo;
  21.     unsigned char base_mid;
  22.     unsigned char type_dpl;
  23.     unsigned char limit_hi;
  24.     unsigned char base_hi;
  25. }descriptor;
  26.  
  27. /* call, task, interrupt, trap gate */
  28.  
  29. typedef struct gate{
  30.     unsigned int offset_lo;
  31.     unsigned int selector;
  32.     unsigned char count;
  33.     unsigned char type_dpl;
  34.     unsigned int offset_hi;
  35. }gate;
  36.  
  37. /* this is the layout for a task state segment  (TSS) */
  38. /* the fill fields of structures are not used by the 80386 */
  39. /* but must be there */
  40.  
  41. typedef struct tss{
  42.     unsigned int back_link;     /* selector for last task */
  43.     unsigned int fill1;
  44.     unsigned long esp0;         /* stack pointer privilege level 0 */
  45.     unsigned int ss0;           /* stack segment privilege level 0 */
  46.     unsigned int fill2;
  47.     unsigned long esp1;         /* stack pointer privilege level 1 */
  48.     unsigned int ss1;           /* stack segment privilege level 1 */
  49.     unsigned int fill3;
  50.     unsigned long esp2;         /* stack pointer privilege level 2 */
  51.     unsigned int ss2;           /* stack segment privilege level 2 */
  52.     unsigned int fill4;
  53.     unsigned long cr3;          /* control register 3, page table */
  54.     unsigned long eip;          /* instruction pointer */
  55.     unsigned long eflags;
  56.     unsigned long eax;
  57.     unsigned long ecx;
  58.     unsigned long edx;
  59.     unsigned long ebx;è    unsigned long esp;
  60.     unsigned long ebp;
  61.     unsigned long esi;
  62.     unsigned long edi;
  63.     unsigned int es;
  64.     unsigned int fill5;
  65.     unsigned int cs;
  66.     unsigned int fill6;
  67.     unsigned int ss;
  68.     unsigned int fill7;
  69.     unsigned int ds;
  70.     unsigned int fill8;
  71.     unsigned int fs;
  72.     unsigned int fill9;
  73.     unsigned int gs;
  74.     unsigned int filla;
  75.     unsigned int ldt;
  76.     unsigned int fillb;
  77.     unsigned int tbit;          /* exception on task switch bit */
  78.     unsigned int iomap;
  79. }tss;
  80.  
  81. #define TSS_SIZE            (sizeof(tss))
  82. #define DESCRIPTOR_SIZE     (sizeof(descriptor))
  83. #define GATE_SIZE           (sizeof(gate))
  84. #define DPL(x)              (x<<5)
  85. #define TYPE_CODE_DESCR     0x18
  86. #define TYPE_DATA_DESCR     0x10
  87. #define TYPE_TSS_DESCR      0x09
  88. #define TYPE_CALL_GATE      0x0c
  89. #define TYPE_TASK_GATE      0x05
  90. #define TYPE_INTERRUPT_GATE 0x0e
  91. #define TYPE_TRAP_GATE      0x0f
  92. #define SEG_WRITABLE        0x02
  93. #define SEG_READABLE        0x02
  94. #define SEG_EXPAND_DOWN     0x04
  95. #define SEG_CONFORMING      0x04
  96. #define SEG_ACCESSED        0x01
  97. #define SEG_TASK_BUSY_BIT   0x02
  98. #define SEG_PRESENT_BIT     0x80
  99. #define SEG_GRANULARITY_BIT 0x80
  100. #define SEG_DEFAULT_BIT     0x40
  101. #define SELECTOR_MASK       0xfff8
  102.  
  103. [LISTING TWO]
  104.  
  105. /************************************************************************/
  106. /*      TASK.C - this code creates and sets up the Global Descriptor    */
  107. /*      Table and Task State Segments. the code switches to protected   */
  108. /*      mode, runs tasks, and returns to real mode.                     */
  109. /*      Compile with Turbo C 2.0                                        */
  110. /*      By Tom Green                                                    */
  111. /************************************************************************/
  112.  
  113. #include <stdio.h>
  114. #include <dos.h>
  115. #include <conio.h>
  116. #include <stdlib.h>
  117. #include "386.h"
  118.  
  119. /* selectors for entries in our GDT */
  120. #define CODE_SELECTOR       0x08
  121. #define DATA_SELECTOR       0x10
  122. #define TASK_1_SELECTOR     0x18
  123. #define TASK_2_SELECTOR     0x20
  124. #define MAIN_TASK_SELECTOR  0x28
  125. #define VID_MEM_SELECTOR    0x30
  126.  
  127. /* physical address of video ram, mono and color */
  128. #define COLOR_VID_MEM       0xb8000L
  129. #define MONO_VID_MEM        0xb0000L
  130.  
  131. /* video modes returned by BIOS call */
  132. #define MONO_MODE           0x07
  133. #define BW_80_MODE          0x02
  134. #define COLOR_80_MODE       0x03
  135.  
  136. /* pointer to a function */
  137. typedef void (func_ptr)(void);
  138.  
  139. /* extern stuff in mode.asm */
  140. void protected_mode(unsigned long gdt_ptr,unsigned int cseg,unsigned int dseg);
  141. unsigned int load_task_register(unsigned int tss_selector);
  142. void real_mode(unsigned int dseg);
  143. void jump_to_task(unsigned int tss_selector);
  144.  
  145.  
  146. /* prototypes for local functions */
  147. void task1(void);
  148. void task2(void);
  149. void init_tss(tss *t,unsigned int cs,unsigned int ds,unsigned char *sp,
  150.                 func_ptr ip);
  151. void init_gdt_descriptor(descriptor *descr,unsigned long base,unsigned long
  152.                             limit,unsigned char type);
  153. void print(unsigned int x,unsigned int y,char *s);
  154. void vid_mem_putchar(unsigned int x,unsigned int y,char c);
  155.  
  156. /* this array of descriptors will be our Global Descriptor Table */
  157. descriptor gdt[10];
  158.  
  159. /* these are the TSS's for our tasks */ètss main_tss;
  160. tss task_1_tss;
  161. tss task_2_tss;
  162.  
  163. /* seperate stacks for each task */
  164. unsigned char task_1_stack[1024];
  165. unsigned char task_2_stack[1024];
  166.  
  167.  
  168. /* global y location for protected mode screen writes */
  169. /* using descriptor for video ram */
  170. unsigned int y=0;
  171.  
  172. void main(void)
  173. {
  174.     unsigned long base;
  175.     unsigned char type;
  176.     union REGS r;
  177.  
  178.     /* setup code and data descriptors in GDT */
  179.  
  180.     /* code GDT entry 1 */
  181.  
  182.     /* turn code segment into 20 (and 32) bit physical base address */
  183.     base=((unsigned long)_CS)<<4;
  184.     /* set descriptor type for a readable code segment */
  185.     type=TYPE_CODE_DESCR | SEG_PRESENT_BIT | SEG_READABLE;
  186.     init_gdt_descriptor(&gdt[1],base,0xffffL,type);
  187.  
  188.     /* data GDT entry 2 */
  189.  
  190.     /* turn data segment into 20 (and 32) bit physical base address */
  191.     base=((unsigned long)_DS)<<4;
  192.     /* set descriptor type for a writeable data segment */
  193.     type=TYPE_DATA_DESCR | SEG_PRESENT_BIT | SEG_WRITABLE;
  194.     init_gdt_descriptor(&gdt[2],base,0xffffL,type);
  195.  
  196.     /* set up TSS's for tasks here */
  197.  
  198.     /* set descriptor type for a TSS */
  199.     type=TYPE_TSS_DESCR | SEG_PRESENT_BIT;
  200.  
  201.     /* put a descriptor for each TSS in the GDT */
  202.  
  203.     /* TSS GDT entry 3, TSS for task1 */
  204.     /* turn segment:offset of task1 TSS into physical base address */
  205.     base=(((unsigned long)_DS)<<4)+(unsigned int)&task_1_tss;
  206.     init_gdt_descriptor(&gdt[3],base,(unsigned long)TSS_SIZE-1,type);
  207.  
  208.     /* TSS GDT entry 4, TSS for task2 */
  209.     /* turn segment:offset of task2 TSS into physical base address */
  210.     base=(((unsigned long)_DS)<<4)+(unsigned int)&task_2_tss;
  211.     init_gdt_descriptor(&gdt[4],base,(unsigned long)TSS_SIZE-1,type);
  212.  
  213.     /* TSS GDT entry 5, TSS for main starting task */è    /* turn segment:offset of main TSS into physical base address */
  214.     base=(((unsigned long)_DS)<<4)+(unsigned int)&main_tss;
  215.     init_gdt_descriptor(&gdt[5],base,(unsigned long)TSS_SIZE-1,type);
  216.  
  217.     /* init the TSS with starting values for each task */
  218.  
  219.     /* task 1 */
  220.     init_tss(&task_1_tss,CODE_SELECTOR,DATA_SELECTOR,task_1_stack+
  221.                 sizeof(task_1_stack),task1);
  222.  
  223.     /* task 2 */
  224.     init_tss(&task_2_tss,CODE_SELECTOR,DATA_SELECTOR,task_2_stack+
  225.                 sizeof(task_2_stack),task2);
  226.  
  227.     /* video ram descriptor GDT entry 6 */
  228.  
  229.     /* set descriptor for a writeable data segment */
  230.     type=TYPE_DATA_DESCR | SEG_PRESENT_BIT | SEG_WRITABLE;
  231.     r.h.ah=15;      /* get video mode BIOS */
  232.     int86(0x10,&r,&r);
  233.     /* check if mono mode */
  234.     if(r.h.al==MONO_MODE)
  235.         init_gdt_descriptor(&gdt[6],MONO_VID_MEM,3999,type);
  236.     /* check if color mode */
  237.     else if(r.h.al==BW_80_MODE || r.h.al==COLOR_80_MODE)
  238.         init_gdt_descriptor(&gdt[6],COLOR_VID_MEM,3999,type);
  239.     else{
  240.         printf("\nThis video mode is not supported.");
  241.         exit(1);
  242.     }
  243.  
  244.     /* we are now ready to enter protected mode */
  245.  
  246.     clrscr();
  247.     cprintf("\nPress return to enter protected mode.");
  248.     getchar();
  249.  
  250.     /* turn segment:offset of GDT into 20 bit physical address */
  251.     base=(((unsigned long)_DS)<<4)+(unsigned int)&gdt;
  252.  
  253.     /* this puts us in protected mode */
  254.     protected_mode(base,CODE_SELECTOR,DATA_SELECTOR);
  255.  
  256.     /* this loads the task register for the first task */
  257.     load_task_register(MAIN_TASK_SELECTOR);
  258.  
  259.     y=3;    /* this is line we will start printing on in protected mode */
  260.             /* using our descriptor to write to video ram */
  261.  
  262.     print(0,y++,"Entered protected mode in main task");
  263.  
  264.     /* this jumps to first task (which will jump back here eventually) */
  265.     jump_to_task(TASK_1_SELECTOR);
  266.  
  267.     print(0,y++,"Returned to main task, leaving protected mode");è
  268.     /* return us to real mode */
  269.     real_mode(DATA_SELECTOR);
  270.  
  271.     gotoxy(1,22);
  272.     cprintf("Returned to real mode. Press return to exit to DOS");
  273.     getchar();
  274.     clrscr();
  275. }
  276.  
  277. /* code for task 1 */
  278.  
  279. void task1(void)
  280. {
  281.     while(1){
  282.         print(0,y++,"Hello from task1");
  283.         jump_to_task(TASK_2_SELECTOR);
  284.         /* return to original task (in main()) after several task switches */
  285.         if(y>18)
  286.             jump_to_task(MAIN_TASK_SELECTOR);
  287.     }
  288. }
  289.  
  290. /* code for task 2 */
  291.  
  292. void task2(void)
  293. {
  294.     while(1){
  295.         print(0,y++,"Hello from task2");
  296.         jump_to_task(TASK_1_SELECTOR);
  297.     }
  298. }
  299.  
  300. /* this initializes a TSS */
  301. /* inits segment registers, eip, and stack stuff to starting values */
  302.  
  303. void init_tss(tss *t,unsigned int cs,unsigned int ds,unsigned char *sp,
  304.                 func_ptr ip)
  305. {
  306.     t->cs=cs;                   /* code selector */
  307.     t->ds=ds;                   /* set these to the data selector */
  308.     t->es=ds;
  309.     t->ss=ds;
  310.     t->fs=ds;
  311.     t->gs=ds;
  312.     t->eip=(unsigned int)ip;    /* address of first instruction to execute */
  313.     t->esp=(unsigned int)sp;    /* offset of stack in data */
  314.     t->ebp=(unsigned int)sp;
  315. }
  316.  
  317. /* this initializes a descriptor in the Global Decsriptor Table */
  318. /* sets up the base, limit, type, and granularity */
  319.  
  320. void init_gdt_descriptor(descriptor *descr,unsigned long base,unsigned long
  321.                             limit,unsigned char type)è{
  322.     descr->base_lo=(unsigned int)base;
  323.     descr->base_mid=(unsigned char)(base >> 16);
  324.     descr->type_dpl=type;
  325.     /* if limit > 0xfffffL then we have to set granularity bit and shift */
  326.     if(limit > 0xfffffL){
  327.         limit = limit >> 12;
  328.         descr->limit_hi=((unsigned char)(limit >> 16) & 0xff) |
  329.             SEG_GRANULARITY_BIT;
  330.     }
  331.     else
  332.         descr->limit_hi=((unsigned char)(limit >> 16) & 0xff);
  333.     descr->limit_lo=(unsigned int)limit;
  334.     descr->base_hi=(unsigned char)(base >> 24);
  335. }
  336.  
  337. /* this routine prints a string using vid_mem_putchar */
  338.  
  339. void print(unsigned int x,unsigned int y,char *s)
  340. {
  341.     while(*s)
  342.         vid_mem_putchar(x++,y,*s++);
  343. }
  344.  
  345. /* this routine writes a character directly to video ram */
  346. /* uses the selector for the descriptor we set up in main */
  347. /* for video ram */
  348.  
  349. void vid_mem_putchar(unsigned int x,unsigned int y,char c)
  350. {
  351.     register unsigned int offset;
  352.     char far *vid_ptr;
  353.  
  354.     offset=(y*160) + (x*2);
  355.     /* make our far pointer use our special video ram descriptor */
  356.     /* yes, we can even use far pointers with selectors */
  357.     vid_ptr=MK_FP(VID_MEM_SELECTOR,offset);
  358.     *vid_ptr++=c;       /* write character */
  359.     *vid_ptr=0x07;      /* write attribute byte */
  360. }
  361.  
  362.  
  363. [LISTING THREE]
  364.  
  365. ;*****************************************************************
  366. ;   MODE.ASM
  367. ;   Routines for switching to protected and real mode. Also includes
  368. ;   routines for loading task register and jumping to a task.
  369. ;   Assemble with Turbo TASM 1.0
  370. ;   By Tom Green
  371. ;*****************************************************************
  372.  
  373.     .MODEL SMALL
  374.  
  375.     .386P
  376.  
  377.     .DATA
  378.  
  379. ;this is where we stuff address of GDT that is passed
  380. gdtptr  LABEL   PWORD
  381.     dw  50h             ;size in bytes of GDT, enough for 10 entries
  382.     dd  ?               ;this is where we will put physical address of GDT
  383.  
  384. ;this is where we will store the address of a task (the selector) that
  385. ;we will jump to in jump_to_task
  386. new_task    LABEL   DWORD
  387.     dw  00h
  388. new_select  LABEL   WORD
  389.     dw  00h
  390.  
  391. ;this is where we store address to jump to when we enter protected mode
  392. ;in protected_mode
  393. ;offset of code where we will jump
  394. p_mode          LABEL   DWORD
  395.     dw  OFFSET protect
  396. ;put selector of protected mode code segment here
  397. p_mode_select   LABEL   WORD
  398.     dw  0
  399.  
  400.     .CODE
  401.  
  402.     PUBLIC  _real_mode,_protected_mode,_jump_to_task
  403.     PUBLIC  _load_task_register
  404.  
  405. ;*****************************************************************
  406. ;   void protected_mode(unsigned long gdt_ptr,unsigned int cseg,
  407. ;   unsigned int dseg) - puts 386 in protected mode and loads segment
  408. ;   registers with code and data selectors passed (cs and ds
  409. ;   parameters). pass this routine a 32 bit physical address of the
  410. ;   Global Descriptor Table (gdt_ptr parameter). Turns interrupts
  411. ;   off while we run in protected mode.
  412. ;*****************************************************************
  413. _protected_mode PROC    NEAR
  414.         push    bp
  415.         mov     bp,sp
  416.         mov     ax,[bp+4]               ;get low word of address of GDT
  417.         mov     dx,[bp+6]               ;get high word of address of GDT
  418.         mov     WORD PTR gdtptr+4,dx    ;store high word of address of GDT
  419.         mov     WORD PTR gdtptr+2,ax    ;store low word of address of GDTè        mov     ax,[bp+8]               ;get selector for code descriptor
  420.         mov     dx,[bp+10]              ;get selector for data descriptor
  421.         mov     p_mode_select,ax        ;put code selector in our jmp pointer
  422.         mov     eax,0                   ;prepare to zero out eflags
  423.         push    eax
  424.         popfd                           ;zero out eflags
  425.         lgdt    PWORD PTR gdtptr        ;load gdt register with limit and ptr
  426.         mov     eax,cr0
  427.         or      eax,1
  428.         mov     cr0,eax                 ;turn protected mode on
  429.         jmp     DWORD PTR p_mode        ;this will jump to protect through ptr 
  430.                                         ;(cs will be loaded with code selector)
  431. protect:
  432. ;we are now running in protected mode, and we will load segment registers
  433. ;with selectors that look like our code is still in real mode
  434.         mov     ss,dx           ;load segment registers with data selector
  435.         mov     ds,dx
  436.         mov     es,dx
  437.         mov     fs,dx
  438.         mov     gs,dx
  439.         mov     ax,0
  440.         lldt    ax              ;make sure ldt register has 0
  441.         pop     bp
  442.         ret
  443. _protected_mode ENDP
  444.  
  445. ;*****************************************************************
  446. ;   void load_task_register(unsigned int tss_selector) -
  447. ;   loads task register with TSS selector
  448. ;*****************************************************************
  449. _load_task_register PROC    NEAR
  450.         push    bp
  451.         mov     bp,sp
  452.         ltr     [bp+4]      ;load task register with selector for current task
  453.         pop     bp
  454.         ret
  455. _load_task_register ENDP
  456.  
  457. ;*****************************************************************
  458. ;   void real_mode(unsigned int dseg) -
  459. ;   returns 386 to real mode. pass this routine the selector for
  460. ;   a 64k data segment so we can return to real mode. this
  461. ;   routine assumes we are executing from a 64k data segment.
  462. ;   (80386 segment registers must have selector of segment with
  463. ;   64k limit to return to real mode)
  464. ;*****************************************************************
  465. _real_mode  PROC    NEAR
  466.         push    bp
  467.         mov     bp,sp
  468.         mov     ax,[bp+4]           ;get selector for data segment
  469.         mov     ds,ax               ;now make sure all segment registers
  470.         mov     es,ax               ;contain selector to 64k segment
  471.         mov     fs,ax               ;must have this to return to real mode
  472.         mov     gs,ax
  473.         mov     ss,axè        mov     eax,cr0
  474.         and     eax,07ffffffeh
  475.         mov     cr0,eax             ;protected mode off
  476.         jmp     FAR PTR flush       ;flush queue and set cs for real mode
  477.                                     ;now cs will be loaded with correct
  478.                                     ;segment for real mode
  479. flush:
  480.         mov     ax,DGROUP           ;restore data seg registers for real mode
  481.         mov     ds,ax
  482.         mov     ss,ax
  483.         mov     es,ax
  484.         sti                         ;interrupts back on for real mode
  485.         pop     bp
  486.         ret
  487. _real_mode  ENDP
  488.  
  489. ;*****************************************************************
  490. ;   void jump_to_task(unsigned int tss_selector) -
  491. ;   jumps to 386 TSS task. pass this routine the selector of the
  492. ;   TSS of the task you want to jump to.
  493. ;*****************************************************************
  494. _jump_to_task   PROC    NEAR
  495.         push    bp
  496.         mov     bp,sp
  497.         mov     ax,[bp+4]           ;get selector of new task
  498.         mov     new_select,ax       ;store it in pointer
  499.         jmp     DWORD PTR new_task  ;jump to task through selector:offset ptr
  500.         pop     bp
  501.         ret
  502. _jump_to_task   ENDP
  503.  
  504.                 END
  505.  
  506.  
  507.